package gui;

import ptolemy.kernel.Entity;
import ptolemy.kernel.CompositeEntity;
import ptolemy.kernel.util.*;
import ptolemy.actor.gui.*;
import ptolemy.vergil.actor.*;
import ptolemy.actor.*;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Iterator;
import java.util.Collection;
import java.util.List;

import java.io.*;
import java.net.URL;

import MoC.*;
import db.*;
import util.Util;
import simplification.Simplification;
import preprocess.*;
import sdf.SDFVerifier;
import sr.SRVerifier;

//////////////////////////////////////////////////////////////////////////
//// VerifierEffigy
/**
 
@author Ivan Jeukens
@version v 0.2 24/01/2002
*/
public class VerifierEffigy extends Effigy {

    public VerifierEffigy(Workspace workspace) {
        super(workspace);
    }

    public VerifierEffigy(CompositeEntity container, String name)
            throws IllegalActionException, NameDuplicationException {
        super(container, name);
    }

    ///////////////////////////////////////////////////////////////////
    ////                         public methods                    ////

    public static VerifierEffigy newVerifierEffigy(CompositeEntity container,
            URL base, URL in) throws Exception {
        VerifierEffigy effigy =
                new VerifierEffigy(container, container.uniqueName("effigy"));

        // effigy.initialize(in);
        effigy.setModifiable(false);
        effigy.uri.setURL(in);

        return effigy;
    }

    public static VerifierEffigy newVerifierEffigy(CompositeEntity container)
            throws Exception {
        VerifierEffigy effigy =
                new VerifierEffigy(container, container.uniqueName("effigy"));
        effigy.initialize();

        return effigy;
    }

    public void initialize() {
        _mocs = new HashMap();

        _classDB = new ClassDB();
        _classDataDB = new ClassDataDB();
        _atomicActorDB = new AtomicActorDB();
        _compositeActorDB = new CompositeActorDB();

        _classDB.setAtomicActorDataBase(_atomicActorDB);
        _atomicActorDB.setClassDataBase(_classDB);
        _compositeActorDB.setAtomicActorDataBase(_atomicActorDB);

        _simplifier = new Simplification();

        SDFVerifier sdfv = new SDFVerifier();
        _mocs.put(sdfv.getName(), sdfv);

        SRVerifier srv = new SRVerifier();
        _mocs.put(srv.getName(), srv);

        _progress = new ProgressDialog(null);
    }

    // Constroi a effigy a partir do Vergil
    public void analyzeSpec(CompositeEntity actor, URL url,
            LinkedList selectedMoCs) {
        _progress.show();

        LinkedList mocs = new LinkedList();
        Iterator mocite = _mocs.values().iterator();
        while (mocite.hasNext()) {
            MoCVerifier ver = (MoCVerifier) mocite.next();
            if (selectedMoCs.contains(ver.getName())) {
                mocs.add(ver);
            }
        }

        _progress.addMessage("Initializing Data Structures", 2);
        _initializeDataStructures(actor, url);

        _progress.addMessage("Build Execution Methods Representations", 2);
        _buildExecutionMethods();

        _progress.addMessage("Applying Code Simplification", 2);
        _codeSimplification();

        _progress.addMessage("Build Method Graphs", 2);
        _buildMethodGraphs();

        _progress.addMessage("Generating Code", 2);
        _generateCode();

        _progress.addMessage("Iniciating MoC Verification", 2);
        _progress.addMessage("Performing Preprocess", 2);
        _preprocess();
        _progress.addMessage(_preprocessResult.summary(), 2);

        _progress.addMessage("Checking commom restrictions", 2);
        _checkCommomRestrictions();

        _progress.addMessage("Starting MoC verification phase.", 2);
        _mocVerif(mocs);
        // _associateDirectors();
        _progress.addMessage("MoC verification done.", 2);

        for(int i = 0; i < mocs.size(); i++) {
            MoCVerifier mocver = (MoCVerifier) mocs.get(i);
            ResultMessages res = mocver.getResult();
            _progress.addMessage(res.summary(), 2);

            if(mocver.getName().equals("SDF")) {
                _sdfValidationResult = res;
            }
            else
            if(mocver.getName().equals("SR")) {
                _srValidationResult = res;
            }
        }
        _newInfo.debug("final");
        _progress.done();
    }
    
    public void setConfiguration(Configuration configuration) {
        _configuration = configuration;
    }

    // As mensagens para colocar na interface.
    public Iterator preprocessMessagesIterator() {
        return _preprocessResult.messagesIterator();
    }

    public Iterator sdfValidationMessagesIterator() {
        if(_sdfValidationResult == null) return null;
        return _sdfValidationResult.messagesIterator();
    }

    public Iterator srValidationMessagesIterator() {
        if(_srValidationResult == null) return null;
        return _srValidationResult.messagesIterator();
    }

    public Iterator commomRestrictionsMessagesIterator() {
        if(_commomRestrictionsResult == null) return null;
        return _commomRestrictionsResult.messagesIterator();
    }

    public ActorGraphTableau getActorTableau(Entity actor) {
        CompositeEntity ce = (CompositeEntity) actor.getContainer();
        ActorGraphTableau t = null;
        try {
            t = (ActorGraphTableau) _configuration.openModel(ce);
        } catch (Exception ex) {
            System.out.println(ex);
        }
        return t;
    }

    public LinkedList getAllActors(String classname) {
        return (LinkedList) _actorClassMap.get(classname);
    }

    public CompositeActorInfo getInfo() {
        return _newInfo;
    }

    ///////////////////////////////////////////////////////////////////
    ////                         private methods                   ////

    // *************** INICIALIZACAO DAS ESTRUTURAS ***************************
    private void _initializeDataStructures(CompositeEntity toplevel, URL url) {
        _buildActorClassMap(toplevel);
        _progress.addMessage("Building model information.", 1);

        if(_compositeActorDB.contains(toplevel.getClass().getName())) {
            _newInfo = (CompositeActorInfo) _compositeActorDB.getInfo(
                toplevel.getClass().getName());
        }
        else {
            _newInfo = new CompositeActorInfo(toplevel, _compositeActorDB,
                _atomicActorDB, _classDB, _classDataDB);

            // O nome da effigy tem que ser o do ator, para ela
            // ser encontrada depois
            try {
                _newInfo.setURL(url);
                setName( Util.removeDot(
                             new String(url.toString() + _newInfo.getName())));
            }
            catch(IllegalActionException ex) {
                System.out.println(ex);
            }
            catch(NameDuplicationException ex) {
                System.out.println(ex);
            }
            _compositeActorDB.add(_newInfo);
        }

        Iterator ite = _classDB.valuesIterator();
        while(ite.hasNext()) {
            ClassInfo cinfo = (ClassInfo) ite.next();
            cinfo.buildUsedBy(_actorClassMap, _atomicActorDB);
        }

        _progress.addMessage("Done", 1);
    }

    // Constroi uma tabela de hash onde as chaves sao as diferentes
    // classes de atores atomicos, e o dado associado eh uma lista
    // de instancias de atores dessa classe.
    private void _buildActorClassMap(CompositeEntity toplevel) {
        _actorClassMap = new HashMap();

        LinkedList process = new LinkedList();
        process.add(toplevel);
        while(process.size() > 0) {
            CompositeEntity ent = (CompositeEntity) process.removeFirst();
            List l = ent.entityList();
            Iterator ite = l.iterator();
            while (ite.hasNext()) {
                Entity e = (Entity) ite.next();
                String cname = e.getClass().getName();
                if (_actorClassMap.containsKey(cname)) {
                    LinkedList l2 = (LinkedList) _actorClassMap.get(cname);
                    l2.add(e);
                } else {
                    LinkedList l2 = new LinkedList();
                    l2.add(e);
                    _actorClassMap.put(cname, l2);
                }

                if(e instanceof CompositeEntity) {
                    process.add(e);
                }
            }
        }
    }

    // *************** CRIACAO DOS METODOS DE EXECUCAO *********************
    // Depois que esse metodo acaba, cada classe construiu e fez todas
    // as operacoes necessarias sobre todos os fgs.
    private void _buildExecutionMethods() {
        Iterator classes = _classDataDB.valuesIterator();
        while(classes.hasNext()) {
            ClassData cdata = (ClassData) classes.next();
            _progress.addMessage("build flow graphs for " + cdata.getName(), 1);

            cdata.buildMethodFG("fire");
            cdata.buildMethodFG("postfire");
            cdata.buildMethodFG("prefire");
            cdata.buildMethodFG("initialize");
            // operacoes pos construcao.
            cdata.performFGOperations();

            // constroi o restantes flow graphs a partir dos metodos de 
            // execucao
            cdata.buildMCNodesFG();
        }
    }

    // ******************* SIMPLIFICACAO DE CODIGO **************************
    private void _codeSimplification() {
        Iterator classes = _classDataDB.valuesIterator();
        while(classes.hasNext()) {
            ClassData cdata = (ClassData) classes.next();
            cdata.buildSSA();
        }
        classes = _classDataDB.valuesIterator();
        while(classes.hasNext()) {
            ClassData cdata = (ClassData) classes.next();
            _progress.addMessage("Trying to simplify " + cdata.getName(), 1);
            _simplifier.simplify(cdata);
        }
        classes = _classDataDB.valuesIterator();
        while(classes.hasNext()) {
            ClassData cdata = (ClassData) classes.next();
            cdata.markFlowGraphs();
        }
    }

    // ******************* METHOD GRAPHS **************************
    private void _buildMethodGraphs() {
        Iterator classes = _classDataDB.valuesIterator();
        while(classes.hasNext()) {
            ClassData cdata = (ClassData) classes.next();
            _progress.addMessage("Creating MG for " + cdata.getName(), 1);
            cdata.buildMethodGraphs();
        }
    }
    
    // ******************* GERACAO DE CODIGO **************************
    private void _generateCode() {
        Iterator classes = _classDataDB.valuesIterator();
        while(classes.hasNext()) {
            ClassData cdata = (ClassData) classes.next();
            _progress.addMessage("generating for " + cdata.getName(), 1);
            cdata.generateCode();
        }
    }

    // ******************** PREPROCESS *******************************
    private void _preprocess() {
        // Faz o preprocessamento sobre todos as INSTANCIAS de atores
        // atomicos.
        Iterator actors = _atomicActorDB.valuesIterator();
        Iterator classes = _classDB.valuesIterator();

        Preprocess p = new Preprocess();
        p.performChecks(actors, classes);

        // propaga o resultado dos atores atomicos que sao validos para PN, CSP
        // e especificos DE e CT para as topologias.
        _propagatePreprocess(_newInfo);

        _preprocessResult = p.getResult();
    }
    
    private void _propagatePreprocess(CompositeActorInfo info) {
        Iterator ite = info.compositeActorsIterator();
        while(ite.hasNext()) {
            CompositeActorInfo cinfo = (CompositeActorInfo) ite.next();
            _propagatePreprocess(cinfo);
        }

        CompositeActorResult compRes = info.getResult();
        boolean notcsp = false, notpn = false;
        HashMap map = new HashMap();
        ite = info.atomicActorsIterator();
        while(ite.hasNext()) {
            AtomicActorInfo ainfo = (AtomicActorInfo) ite.next();
            ClassInfo cinfo = ainfo.getClassInfo();

            if(map.containsKey(cinfo)) continue;
            map.put(cinfo, null);

            ClassResult res = cinfo.getResult();
            if(res.isSpecific()) {
                compRes.setSpecificMoC(res.getSpecificMoC());
                if(!res.isValidFor("CSP")) notcsp = true;
                notpn = true;
                break;
            }
            else
            if(!res.isValidFor("CSP")) {
                notcsp = true;
            }
            else
            if(!res.isValidFor("PN")) {
                notpn = true;
            }
        }
/*
        ite = info.compositeActorsIterator();
        while(ite.hasNext()) {
            CompositeActorInfo cinfo = (CompositeActorInfo) ite.next();
            CompositeActorResult res = cinfo.getResult();
            if(res.isSpecific()) {
                compRes.setSpecificMoC(res.getSpecificMoC());
                if(!res.isValidFor("CSP")) notcsp = true;
                notpn = true;
                break;
            }
            else
            if(!res.isValidFor("CSP")) {
                notcsp = true;
            }
            else
            if(!res.isValidFor("PN")) {
                notpn = true;
            }
        }*/

        if(!notcsp) {
            compRes.addAssociatedDirector("CSP");
        }

        if(!notpn) {
            compRes.addAssociatedDirector("PN");
        }
    }

    // **************** RESTRICOES COMUNS A TODOS OS MOCS  *******************
    private void _checkCommomRestrictions() {
        _commomRestrictionsResult = new ResultMessages("Commom Restrictions");
        CommonMoCRestrictions res = new CommonMoCRestrictions(
            _commomRestrictionsResult);

        Iterator ite = _classDB.valuesIterator();
        while(ite.hasNext()) {
            ClassInfo cinfo = (ClassInfo) ite.next();
            ClassResult cres = cinfo.getResult();
            if(cres.toVerify("SDF") || cres.toVerify("SR")) {
                _progress.addMessage("Checking commom restrictions for " +
                    cinfo.getName(), 1);
                if(res.check(cinfo)) {    // eh invalido, entao nao verifica nada
                    cres.removeToVerify("SDF");
                    cres.removeToVerify("SR");
                }
            }
        }
    }

    // ***************  VALIDACAO DOS MODELOS  *******************************
    // primero analisa todos os atores atomicos, para em seguida, processar
    // os composite da maneira bottom up.
    private void _mocVerif(LinkedList mocs) {
        Iterator classes = _classDB.valuesIterator();
        while(classes.hasNext()) {
            ClassInfo cinfo = (ClassInfo) classes.next();
            for(int i = 0;i < mocs.size();i++) {
                MoCVerifier mocver = (MoCVerifier) mocs.get(i);
                mocver.analyze(cinfo, _progress);
            }
        }

        try {
            CompositeActor top = (CompositeActor) _newInfo.getEntity();
            Manager manager = new Manager(top.workspace(), "manager");
            top.setManager(manager);
        }
        catch(Exception ex) {
            ex.printStackTrace();
        }

        _mocVerif(_newInfo, mocs);
    }

    private void _mocVerif(CompositeActorInfo info, LinkedList mocs) {
        Iterator ite = info.compositeActorsIterator();
        while(ite.hasNext()) {
            CompositeActorInfo cinfo = (CompositeActorInfo) ite.next();
            _mocVerif(cinfo, mocs);
        }
        _checkMocs(info, mocs);
    }

    // Verifica um ator composite
    private void _checkMocs(CompositeActorInfo info, LinkedList mocs) {
        for(int i = 0; i < mocs.size(); i++) {
            MoCVerifier mocver = (MoCVerifier) mocs.get(i);
            mocver.analyze(info, _progress);
        }
    }
    
    //  A partir de um nivel da topologia, atribui a todos os subniveis
    // que podem ser associados a um diretor SDF.
    private void _associateDirectors() {
        CompositeActorResult res = _newInfo.getResult();
        if(!res.isSpecific()) {
            if(res.isSDFSchedulable()) {
                res.addAssociatedDirector("SDF");
            }

            if(res.isValidAsAtomicActorFor("SR")) {
                res.addAssociatedDirector("SR");
            }
        }

        Iterator ite = _newInfo.compositeActorsIterator();
        while(ite.hasNext()) {
            CompositeActorInfo subinfo = (CompositeActorInfo) ite.next();
            _associateDirectors(subinfo);
        }
    }

    private void _associateDirectors(CompositeActorInfo cinfo) {
        CompositeActorResult res = cinfo.getResult();
        if(!res.isSpecific()) {
            if(res.isValidAsAtomicActorFor("SDF")) {
                res.addAssociatedDirector("SDF");
            }

            if(res.isValidAsAtomicActorFor("SR")) {
                res.addAssociatedDirector("SR");
            }

            if(!res.isValidAsAtomicActorFor("SDF") && 
                    !res.isValidAsAtomicActorFor("SR") &&
                    res.isSDFSchedulable()) {
                res.addAssociatedDirector("Controlable by SDF");
            }
        }

        Iterator ite = cinfo.compositeActorsIterator();
        while(ite.hasNext()) {
            CompositeActorInfo subinfo = (CompositeActorInfo) ite.next();
            _associateDirectors(subinfo);
        }
    }

    ///////////////////////////////////////////////////////////////////
    ////                         private variables                 ////

    // Os resultados de cada etapa
    private ResultMessages _preprocessResult;
    private ResultMessages _sdfValidationResult;
    private ResultMessages _srValidationResult;
    private ResultMessages _commomRestrictionsResult;

    // A estrutura de dados da especificacao sendo analisada
    private CompositeActorInfo _newInfo = null;

    // Para cada classe de ator, associa uma lista de instancias de atores
    // dessa classe
    private HashMap _actorClassMap;

    private Configuration _configuration;

    private ProgressDialog _progress;

    // Quais sao os MoCs e as respectivas classes de seus verificadores
    private HashMap _mocs;

    private Simplification _simplifier;
    
    // As databases
    private ClassDataDB _classDataDB;
    private ClassDB _classDB;
    private AtomicActorDB _atomicActorDB;
    private CompositeActorDB _compositeActorDB;
    
    ///////////////////////////////////////////////////////////////////
    ////                         inner classes                     ////

    public static class Factory extends EffigyFactory {

        public Factory(CompositeEntity container, String name)
                throws IllegalActionException, NameDuplicationException {
            super(container, name);
        }

        ///////////////////////////////////////////////////////////////
        ////                     public methods                    ////

        public boolean canCreateBlankEffigy() {
            return false;
        }

        public Effigy createEffigy(CompositeEntity container)
                throws Exception {
            return newVerifierEffigy(container);
        }

        public Effigy createEffigy(CompositeEntity container, URL base, URL in)
                throws Exception {
            if (in != null) {
                    String extension = getExtension(in);
                    if (!extension.equals("ver")) {
                            return null;
                    }
            }
            return newVerifierEffigy(container, base, in);
        }
    }
}
